﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Newtonsoft.Json;
namespace Ezviz.OpenSDK
{
    /// <summary>
    /// 开平SDK
    /// </summary>
    public class OpenSDK
    {
        internal static IDictionary<int, OpenSDKSession> SESSIONS;

        /// <summary>
        /// APP KEY
        /// </summary>
        private static string APPKEY;

        static OpenSDK()
        {
            SESSIONS = new Dictionary<int, OpenSDKSession>();
        }

        /// <summary>
        /// 初始化SDK
        /// </summary>
        /// <param name="appKey"></param>
        /// <returns>0表示成功，-1表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int InitSDK(string appKey)
        {
            int result = OpenNetStream.OpenSDK_Init(appKey);
            if (result == OpenSDKErrorCode.OK)
                APPKEY = appKey;
            return result;
        }
        /// <summary>
        /// 释放SDK
        /// </summary>
        /// <returns>0表示成功，-1表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int FreeSDK()
        {
            int result = OpenNetStream.OpenSDK_FiniLib();
            if (result == OpenSDKErrorCode.OK)
                SESSIONS.Clear();
            return result;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        private OpenSDK()
        {
        }

        /// <summary>
        /// AppKey
        /// </summary>
        public static string AppKey
        {
            get
            {
                return APPKEY;
            }
        }

        /// <summary>
        /// 分配会话
        /// </summary>
        /// <param name="sync">是否同步,默认异步</param>
        /// <param name="timeOut">超时时间(毫秒)</param>
        /// <returns>成功则返回<see cref="OpenSDKSession"/>，失败返回Null</returns>
        public static OpenSDKSession AllocSession(bool sync = false, uint timeOut = 0xEA60)
        {
            return AllocSession(IntPtr.Zero, sync, timeOut);
        }
        /// <summary>
        /// 分配会话
        /// </summary>
        /// <param name="userData">用户数据</param>
        /// <param name="sync">是否异步</param>
        /// <param name="timeOut">超时时间(毫秒)</param>
        /// <returns>成功则返回<see cref="OpenSDKSession"/>，失败返回Null</returns>
        public static OpenSDKSession AllocSession(IntPtr userData, bool sync = false, uint timeOut = 0xEA60)
        {
            OpenSDKSession openSDKSession = new OpenSDKSession(sync);
            IntPtr session = IntPtr.Zero;
            int len = default(int);

            int result = OpenNetStream.OpenSDK_AllocSession(OpenSDKSession.MessageHandler,userData, ref session, ref len, sync, timeOut);
            if (result != OpenSDKErrorCode.OK)
            {
                //如果分配了会话则释放
                if (session != IntPtr.Zero)
                    OpenNetStream.OpenSDK_FreeSession(session);
                return null;
            }
            openSDKSession.Session = session;
            int sessionKey =Marshal.ReadInt32(session);
            SESSIONS[sessionKey] = openSDKSession;
            return openSDKSession;
        }

        /// <summary>
        ///  中间登陆
        /// </summary>
        /// <returns>0表示成功，-1表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int MidLogin(ref string accessToken)
        {
            IntPtr accessTokenIntPtr = IntPtr.Zero;
            int accessTokenLen = 0;
            int result = OpenNetStream.OpenSDK_Mid_Login(ref accessTokenIntPtr, ref accessTokenLen);
            if (result==OpenSDKErrorCode.OK)
                accessToken = Marshal.PtrToStringAnsi(accessTokenIntPtr, accessTokenLen);
            if (accessTokenIntPtr != IntPtr.Zero)
                OpenNetStream.OpenSDK_Data_Free(accessTokenIntPtr);
            return result;
        }

        /// <summary>
        /// 设备添加
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <returns>0表示成功，非0表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int MidDeviceAdd(string accessToken)
        {
            return OpenNetStream.OpenSDK_Mid_Device_Add(accessToken);
        }

        /// <summary>
        /// 设备操作
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="pageIndex">分页起始页，从0开始</param>
        /// <param name="pageSize"> 分页大小</param>
        /// <returns>0表示成功，-1表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int MidDeviceOper(string accessToken, string deviceId)
        {
            return OpenNetStream.OpenSDK_Mid_Device_Oper(accessToken, deviceId);
        }

        /// <summary>
        /// 获取设备列表信息
        /// </summary>
        /// <param name="accessToken">AccessToken</param>
        /// <param name="pageIndex">起始位置</param>
        /// <param name="pageSize">页大小</param>
        /// <returns>成功则返回<see cref="EZDeviceListReponse"/>，失败返回Null</returns>
        public static EZDeviceListReponse GetDevices(string accessToken, int pageIndex = 0, int pageSize = 50)
        {
            IntPtr buf = IntPtr.Zero;
            int bufLen = 0;
            int result =OpenNetStream.OpenSDK_Data_GetDevList(accessToken, pageIndex, pageSize, ref buf, ref bufLen);
            if (result!=OpenSDKErrorCode.OK)
                return null;
            string response = null;
            if (buf != IntPtr.Zero)
            {
                response = Marshal.PtrToStringAnsi(buf, bufLen);
                OpenNetStream.OpenSDK_Data_Free(buf);
            }
            if (string.IsNullOrEmpty(response))
                return null;
            return FromJson<EZDeviceListReponse>(response);
        }

        ///// <summary>
        ///// 获取摄像头信息
        ///// </summary>
        ///// <param name="accessToken">认证Token</param>
        ///// <param name="deviceSerial">设备序列号</param>
        ///// <returns>成功则返回<see cref="CameraResponse"/>，失败返回Null</returns>
        //public static CameraResponse GetCamera(string accessToken,string deviceSerial)
        //{
        //    IntPtr buf = IntPtr.Zero;
        //    int bufLen = 0;
        //    int result = OpenNetStream.OpenSDK_Data_GetDeviceInfo(accessToken, deviceSerial, ref buf, ref bufLen);
        //    if (result!= OpenSDKErrorCode.OK)
        //        return null;
        //    string response = null;
        //    if (buf != IntPtr.Zero)
        //    {
        //        response = Marshal.PtrToStringAnsi(buf, bufLen);
        //        OpenNetStream.OpenSDK_Data_Free(buf);
        //    }
        //    if (string.IsNullOrEmpty(response))
        //        return null;
        //    return FromJson<CameraResponse>(response);
        //}

        /// <summary>
        ///  获取报警列表
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="cameraId">监控点Id</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="alarmType">报警类型</param>
        /// <param name="status">报警状态，0表示未读，1表示已读，2表示所有</param>
        /// <param name="pageIndex">分页起始页，从0开始</param>
        /// <param name="pageSize">分页大小</param>
        /// <returns>成功则返回<see cref="EZAlarmListReponse"/>，失败返回Null</returns>
        public static EZAlarmListReponse GetAlarmList(string accessToken, string cameraId, DateTime startTime, DateTime endTime, EZAlarmType alarmType, EZAlarmStatus status, int pageIndex = 0, int pageSize = 50)
        {
            IntPtr buf = IntPtr.Zero;
            int bufLen = 0;
            int result = OpenNetStream.OpenSDK_Data_GetAlarmList(accessToken, cameraId, startTime.ToString("yyyy-MM-dd hh:mm:ss"), endTime.ToString("yyyy-MM-dd hh:mm:ss"), alarmType, status, pageIndex, pageSize, ref buf, ref bufLen);
            string response = ReadStringAndFree(buf, bufLen);
            if (result != OpenSDKErrorCode.OK)
                return null;
            if (string.IsNullOrEmpty(response))
                return null;
            return FromJson<EZAlarmListReponse>(response);
        }

        /// <summary>
        /// 设置告警消息以及推送消息
        /// </summary>
        /// <param name="alarmMessageHandler">告警推送函数 </param>
        /// <param name="userData">用户自定义数据</param>
        /// <returns>0表示成功，-1表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int SetAlarmMsgCallBack(AlarmMessageHandler alarmMessageHandler,IntPtr userData)
        {
            return OpenNetStream.OpenSDK_Alarm_SetMsgCallBack(alarmMessageHandler, null,userData);
        }

        /// <summary>
        /// 设置消息透传回调
        /// </summary>
        /// <param name="alarmTransparentHandler">消息透传处理器</param>
        /// <param name="userData">用户自定义数据</param>
        /// <returns>0表示成功，-1表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int SetAlarmTransparentCallBack(AlarmTransparentHandler alarmTransparentHandler, IntPtr userData)
        {
            return OpenNetStream.OpenSDK_Alarm_SetTransparentCallBack(alarmTransparentHandler, userData);
        }

        /// <summary>
        /// 设置告警通知回调
        /// </summary>
        /// <param name="alarmNotifyHandler">警情通知处理器</param>
        /// <param name="userData">用户自定义数据</param>
        /// <returns>0表示成功，-1表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int SetNotifyCallBack(AlarmNotifyHandler alarmNotifyHandler, IntPtr userData)
        {
            return OpenNetStream.OpenSDK_Alarm_SetNotifyCallBack(alarmNotifyHandler, userData);
        }
        /// <summary>
        /// 开始接受告警
        /// </summary>
        /// <param name="accessToken">认证token</param>
        /// <returns>0表示成功，-1表示失败, -2 已经连接成功, -3 正在重连 </returns>
        public static int StartRecvAlarm(string accessToken)
        {
            return OpenNetStream.OpenSDK_Alarm_StartRecv(accessToken);
        }
        /// <summary>
        ///  停止接收告警
        /// </summary>
        /// <returns>0表示成功，-1表示失败<see cref="OpenSDKErrorCode"/></returns>
        public static int StopRecvAlarm()
        {
            return OpenNetStream.OpenSDK_Alarm_StopRecv();
        }
        /// <summary>
        /// 获取单个设备信息
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="deviceSerial">设备序列号 </param>
        /// <returns>成功则返回<see cref="EZAlarmListReponse"/>，失败返回Null</returns>
        public static EZDevice GetSingleDevice(string accessToken, string deviceSerial)
        {
            IntPtr buf = IntPtr.Zero;
            int bufLen = 0;
            int result = OpenNetStream.OpenSDK_GetSingleDeviceInfo(accessToken, deviceSerial, ref buf, ref bufLen);
            string response = ReadStringAndFree(buf, bufLen);
            if (result != OpenSDKErrorCode.OK)
                return null;
            if (string.IsNullOrEmpty(response))
                return null;
            return FromJson<EZDevice>(response);
        }

        //从IntPtr中读取字符串并释放IntPtr
        private static string ReadStringAndFree(IntPtr buf, int len)
        {
            if (buf == IntPtr.Zero)
                return null;
            string response = Marshal.PtrToStringAnsi(buf, len);
            OpenNetStream.OpenSDK_Data_Free(buf);
            return response;
        }

        /// <summary>
        /// 对设备布撤防
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="deviceSerial">设备序列号</param>
        /// <param name="defence">布撤防, true 布防; false 撤防</param>
        /// <returns>0表示成功，非0表示失败</returns>
        public static int DefenceDeviceBySerial(string accessToken, string deviceSerial, bool defence)
        {
            int result= OpenNetStream.OpenSDK_DefenceDeviceBySerial(accessToken, deviceSerial, defence);
            if (result == OpenSDKErrorCode.OK)
                return result;
            return GetLastErrorCode();
        }
        /// <summary>
        /// 添加设备
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="deviceSerial">设备序列号</param>
        /// <param name="safeKey">设备验证码</param>
        /// <returns>0表示成功，非0表示失败</returns>
        public static int AddDevice(string accessToken, string deviceSerial, string safeKey="ABCDEF")
        {
            int result=OpenNetStream.OpenSDK_AddDevice(accessToken, deviceSerial, safeKey);
            if (result == OpenSDKErrorCode.OK)
                return result;
            return GetLastErrorCode();
        }

        /// <summary>
        /// 更新监控点详细信息到缓存
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="cameraId">监控点ID</param>
        /// <param name="isEncrypt">监控点设备是否加密</param>
        /// <returns>0表示成功，其他值表示失败</returns>
        public static int UpdateCameraInfoToLocal(string accessToken, string cameraId, ref bool isEncrypt)
        {
            return OpenNetStream.OpenSDK_UpdateCameraInfoToLocal(accessToken, cameraId,ref isEncrypt);
        }

        /// <summary>
        /// 删除设备
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="deviceId">设备Id</param>
        /// <returns>0表示成功，非0表示失败</returns>
        public static int DeleteDevice(string accessToken, string deviceId)
        {
            int result = OpenNetStream.OpenSDK_Data_DeleteDevice(accessToken,deviceId);
            if (result == OpenSDKErrorCode.OK)
                return result;
            return GetLastErrorCode();
        }

        /// <summary>
        ///  获取开通萤石云服务的短信验证码
        /// </summary>
        /// <param name="phone">开通萤石服务账号手机号码</param>
        /// <returns>0表示成功，非0表示失败</returns>
        public static int GetAuthSmsOfYSService(string phone)
        {
            int result= OpenNetStream.OpenSDK_GetAuthSmsOfYSService(phone);
            if (result == OpenSDKErrorCode.OK)
                return result;
            return GetLastErrorCode();
        }


        /// <summary>
        /// 解析警情
        /// </summary>
        /// <param name="alarm"></param>
        /// <returns></returns>
        public static EZAlarm ResolveAlarm(string alarm)
        {
            return JsonConvert.DeserializeObject<EZAlarm>(alarm);
        }
        public static EZPushDeviceMessage ResolvePushDeviceMessage(string content)
        {
            return JsonConvert.DeserializeObject<EZPushDeviceMessage>(content);
        }

        /// <summary>
        /// 透传萤石云平台接口
        /// </summary>
        /// <param name="phone">开通萤石服务账号手机号码</param>
        /// <param name="smsCode">通过OpenSDK_GetAuthSmsOfYSService接口获取的短信验证码</param>
        /// <returns>0表示成功，非0表示失败</returns>
        public static int OpenYSService(string phone, string smsCode)
        {
            int result= OpenNetStream.OpenSDK_OpenYSService(phone, smsCode);
            if (result == OpenSDKErrorCode.OK)
                return result;
            return GetLastErrorCode();
        }

        /// <summary>
        ///  设置告警推送回调
        /// </summary>
        /// <param name="handle">告警推送函数</param>
        /// <param name="userData">用户自定义数据，回调函数会原样抛出</param>
        /// <returns>0表示成功，-1表示失败</returns>
        public static int SetPushAlarmCallBack(PushMessageHandler handle, IntPtr userData)
        {
            return OpenNetStream.OpenSDK_Push_SetAlarmCallBack(handle, userData);
        }
        /// <summary>
        /// 设置设备上下线状态推送
        /// </summary>
        /// <param name="handle">设备状态推送函数</param>
        /// <param name="userData">用户自定义数据，回调函数会原样抛出</param>
        /// <returns>0表示成功，-1表示失败</returns>
        public static int SetDeviceStatusCallBack(PushMessageHandler handle, IntPtr userData)
        {
            return OpenNetStream.OpenSDK_Push_SetDeviceStatusCallBack(handle, userData);
        }
        /// <summary>
        /// 设置设备上下线状态推送
        /// </summary>
        /// <param name="handle">设备状态推送函数</param>
        /// <param name="userData">用户自定义数据，回调函数会原样抛出</param>
        /// <returns>0表示成功，-1表示失败</returns>
        public static int SetTransparentChannelCallBack(PushMessageHandler handle, IntPtr userData)
        {
            return OpenNetStream.OpenSDK_Push_SetTransparentChannelCallBack(handle, userData);
        }

        /// <summary>
        /// 开始接收推送
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="pushId">接入Push的标识，默认使用开发平台的AppKey</param>
        /// <param name="pushSecret">接入Push所使用密钥，创建应用的时候有平台生成</param>
        /// <returns>ErrorCode</returns>
        public static int StartPushRecv(string accessToken, string pushId, string pushSecret)
        {
            return OpenNetStream.OpenSDK_Push_StartRecv("596372da86f84628945da2476960ed9a", "cf5007cb-860f-424a-8536-cc95f75eb6ed", accessToken);
        }


        /// <summary>
        /// 获取摄像头信息
        /// </summary>
        /// <param name="accessToken">认证Token</param>
        /// <param name="deviceSerial">设备序列号</param>
        /// <returns>成功则返回<see cref="EZCameraResponse"/>，失败返回Null</returns>
        public static EZCameraResponse GetCamera(string accessToken, string deviceSerial)
        {
            IntPtr buf = IntPtr.Zero;
            int bufLen = 0;
            int result = OpenNetStream.OpenSDK_Data_GetDeviceInfo(accessToken, deviceSerial, ref buf, ref bufLen);
            if (result != OpenSDKErrorCode.OK)
                return null;
            string response = null;
            if (buf != IntPtr.Zero)
            {
                response = Marshal.PtrToStringAnsi(buf, bufLen);
                OpenNetStream.OpenSDK_Data_Free(buf);
            }
            if (string.IsNullOrEmpty(response))
                return null;
            return FromJson<EZCameraResponse>(response);
        }

        /// <summary>
        /// 停止接收告警
        /// </summary>
        /// <returns>0表示成功，-1表示失败 </returns>
        public static int StopPushRecv()
        {
            return OpenNetStream.OpenSDK_Push_StopRecv();
        }

        /// <summary>
        /// 获取错误码接口
        /// </summary>
        /// <returns>错误码</returns>
        private static int GetLastErrorCode()
        {
            return OpenNetStream.OpenSDK_Push_StopRecv();
        }

        /// <summary>
        /// 获取错误码描述
        /// </summary>
        /// <returns>错误码描述字段</returns>
        public static string GetLastErrorMessage()
        {
            IntPtr result = OpenNetStream.OpenSDK_GetLastErrorDesc();
            if (result != IntPtr.Zero)
                return Marshal.PtrToStringAnsi(result);
            return null;
        }

        /// <summary>
        /// 将Json转化为对象
        /// </summary>
        /// <typeparam name="TObject"></typeparam>
        /// <param name="json"></param>
        /// <returns></returns>
        private static TObject FromJson<TObject>(string json)
        {
            return JsonConvert.DeserializeObject<TObject>(json);
        }
    }
}
